winbrew_app\operations\install/
flow.rs

1//! Orchestration helpers for the middle and final phases of installation.
2//!
3//! The outer install entry point uses this module to keep the overall workflow
4//! readable: the rollback helpers ensure that partial filesystem state is
5//! removed when cancellation or failure interrupts the process, and the engine
6//! execution helper keeps the backend call site consistent once the payload has
7//! already been downloaded and classified.
8//!
9//! The functions here deliberately stay focused on execution and cleanup. They
10//! do not resolve catalog references or translate errors into user-facing types;
11//! that work happens one layer above in [`super::run`].
12
13use anyhow::Result;
14use std::path::Path;
15use tracing::warn;
16
17use super::state;
18use crate::core::fs::{backup_path_for, cleanup_path};
19use crate::engines::{EngineKind, PackageEngine};
20use crate::models::catalog::CatalogInstaller;
21use crate::models::domains::install::EngineInstallReceipt;
22
23/// Remove the temporary root directory used for a single install attempt.
24///
25/// Cleanup failures are logged as warnings rather than returned to the caller,
26/// because install rollback should make a best effort without masking the
27/// original failure that triggered cleanup.
28pub(crate) fn cleanup_temp_root(temp_root: &Path) {
29    if let Err(err) = cleanup_path(temp_root) {
30        warn!(
31            path = %temp_root.display(),
32            error = %err,
33            "failed to clean up temporary install root"
34        );
35    }
36}
37
38/// Roll back the database and filesystem state after an install failure.
39///
40/// The package record is marked as failed and any staged, backup, or final
41/// install directories are removed so the next install attempt starts from a
42/// clean slate.
43pub(crate) fn rollback_failed_install(
44    conn: &crate::database::DbConnection,
45    name: &str,
46    install_dir: &Path,
47) {
48    let _ = state::mark_failed(conn, name);
49    cleanup_install_artifacts(install_dir);
50}
51
52/// Roll back the database and filesystem state after a cancelled install.
53///
54/// Cancellation uses the same cleanup path as a normal failure, but it remains
55/// a separate function so the outer workflow can keep cancellation semantics
56/// explicit.
57pub(crate) fn rollback_cancelled_install(
58    conn: &crate::database::DbConnection,
59    name: &str,
60    install_dir: &Path,
61) {
62    let _ = state::mark_failed(conn, name);
63    cleanup_install_artifacts(install_dir);
64}
65
66/// Execute the selected engine against a downloaded payload.
67pub(crate) fn execute_engine_install(
68    engine: EngineKind,
69    installer: &CatalogInstaller,
70    download_path: &Path,
71    install_dir: &Path,
72    package_name: &str,
73) -> Result<EngineInstallReceipt> {
74    engine.install(installer, download_path, install_dir, package_name)
75}
76
77fn cleanup_install_artifacts(install_dir: &Path) {
78    let stage_dir = install_dir.parent().unwrap_or(install_dir).join("staging");
79    let backup_dir = backup_path_for(install_dir);
80
81    if let Err(err) = cleanup_path(&stage_dir) {
82        warn!(path = %stage_dir.display(), error = %err, "failed to clean up staged install directory");
83    }
84
85    if let Err(err) = cleanup_path(&backup_dir) {
86        warn!(path = %backup_dir.display(), error = %err, "failed to clean up backup install directory");
87    }
88
89    if let Err(err) = cleanup_path(install_dir) {
90        warn!(path = %install_dir.display(), error = %err, "failed to clean up install directory");
91    }
92}